import { ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
import { AfMessageService, AfSecurityService } from '@core';
import { SFIcon, SFLayout, SFSchema } from '@delon/form';
import { SharedModule } from '@shared';
import { delay, of } from 'rxjs';

import { DemoService } from '../demo.service';
/*

*/
@Component({
  selector: 'app-sf-form',
  standalone: true,
  imports: [SharedModule],
  providers: [DemoService],
  templateUrl: './sf-form.component.html',
  styleUrls: ['./sf-form.component.less']
})
export class SfFormComponent implements OnInit {
  selectedIndex = 0;

  actionUrl = '';
  fileList: any[] = [];

  private readonly demoSrv = inject(DemoService);

  token: any = '';
  constructor(
    private msg: AfMessageService,
    private secSrv: AfSecurityService,
    private cdr: ChangeDetectorRef
  ) {}
  ngOnInit(): void {
    this.actionUrl = this.demoSrv.getUploadFileUrl();

    this.cdr.detectChanges();
    this.token = this.secSrv.token;
    console.log('token', this.token);
  }
  // schema: SFSchema = {
  //   properties: {
  //     login_type: {
  //       type: 'string',
  //       title: '登录方式',
  //       enum: [
  //         { label: '手机', value: 'mobile' },
  //         { label: '账密', value: 'account' }
  //       ],
  //       default: 'mobile',
  //       ui: {
  //         widget: 'radio',
  //         styleType: 'button'
  //       }
  //     },
  //     mobile: {
  //       type: 'string',
  //       title: '手机',
  //       ui: {
  //         visibleIf: {
  //           login_type: val => val === 'mobile'
  //         }
  //       }
  //     },
  //     code: {
  //       type: 'number',
  //       title: '验证码',
  //       ui: {
  //         visibleIf: {
  //           login_type: val => val === 'mobile'
  //         }
  //       }
  //     },
  //     name: {
  //       type: 'string',
  //       title: '姓名',
  //       ui: {
  //         visibleIf: {
  //           login_type: val => val === 'account'
  //         }
  //       }
  //     },
  //     pwd: {
  //       type: 'string',
  //       title: '密码',
  //       ui: {
  //         type: 'password',
  //         visibleIf: {
  //           login_type: val => val === 'account'
  //         }
  //       }
  //     }
  //   },
  //   required: ['login_type']
  // };

  schema: SFSchema = {
    properties: {
      id1: { type: 'string', ui: { widget: 'text', defaultText: '' } },
      id2: { type: 'string', ui: { widget: 'text', defaultText: 'default text' } },
      id3: { type: 'string', ui: { widget: 'text', defaultText: '<h2> defaultText </h2>', html: true } },
      name: {
        type: 'string',
        title: '姓名',
        minLength: 5,
        maxLength: 12,
        description: '姓和名字',
        default: 'zhenyun.su',
        ui: {
          width: 350,
          size: 'small',
          optional: '(选填)',
          optionalHelp: {
            text: '长度不能低于5个字符',
            icon: 'edit',
            bgColor: 'red'
          },
          suffix: 'user',
          placeholder: '输入姓名'
        }
      },
      event: {
        type: 'string',
        title: 'event',
        ui: {
          addOnAfter: 'RMB',
          placeholder: 'RMB结算',
          change: (e: any) => console.log(e),
          focus: (e: any) => console.log('focus', e),
          blur: (e: any) => console.log('blur', e),
          enter: (e: any) => console.log('enter', e)
        }
      },
      password: {
        type: 'string',
        title: '密码',
        minLength: 6,
        maxLength: 8,
        readOnly: true,
        ui: {
          type: 'password',
          width: 220
        }
      },
      email: {
        type: 'string',
        title: '邮箱',
        maxLength: 20,
        format: 'email',
        ui: {
          optionalHelp: '请输入邮箱地址',
          placeholder: '邮箱地址'
        }
      },
      phone: {
        type: 'string',
        title: '手机',
        maxLength: 13,
        format: 'mobile',
        ui: {
          addOnBefore: '+86',
          addOnAfter: '*'
        }
      },
      regex: {
        type: 'string',
        pattern: `^[abc]+$`,
        title: '正则表达式',
        ui: {
          placeholder: `^[abc]+$`
        }
      },
      ip: {
        type: 'string',
        title: 'ip',
        format: 'ip'
      },
      'id-card': {
        type: 'string',
        title: '身份证',
        format: 'id-card'
      },
      color: {
        type: 'string',
        title: '颜色',
        format: 'color'
      },
      //告警 util.js:175 strict mode: missing type "number" for keyword "minimum" at "#" (strictTypes)
      //原因：type: 'string', 时，无需设置minimum: 0,
      price: {
        type: 'string',
        title: '价格',
        // minimum: 0,
        default: 25000.11,
        ui: {
          type: 'number',
          optional: '(选填)',
          prefix: '￥',
          suffix: 'RMB',
          placeholder: 'ddd'
        }
      },
      remark: {
        type: 'string',
        title: '描述',
        ui: {
          widget: 'textarea',
          autosize: {
            minRows: 1,
            maxRows: 6
          },
          maxCharacterCount: 100,
          placeholder: '描述信息'
        }
      },
      age: {
        type: 'number',
        title: '年龄',
        minimum: 18,
        maximum: 150,
        default: 20,
        ui: {
          widgetWidth: 120,
          placeholder: '输入年龄'
        }
      },
      salary: {
        type: 'number',
        title: '工资',
        minimum: 3000,
        maximum: 5000000,
        multipleOf: 50,
        default: 5000,
        ui: {
          widgetWidth: '100%',
          prefix: '￥',
          placeholder: '输入工资'
        }
      },
      taxRate: {
        type: 'number',
        title: '税率',
        minimum: 0,
        maximum: 100,
        multipleOf: 1,
        default: 17,
        ui: {
          widgetWidth: 220,
          unit: '%',
          placeholder: '输入工资',
          hideStep: true
        }
      },
      dateTime: {
        type: 'string',
        title: '日期时间',
        format: 'date-time'
      },
      date: {
        type: 'string',
        title: '日期',
        format: 'date'
      },
      time: {
        type: 'string',
        title: '时间',
        format: 'time'
      },
      time_format: {
        type: 'string',
        format: 'time',
        ui: {
          format: `HH:mm:ss+00:00`
        }
      },
      week: {
        type: 'string',
        title: '周',
        format: 'week'
      },
      month: {
        type: 'string',
        title: '月',
        format: 'month'
      },
      dateRange: {
        type: 'string',
        ui: {
          widget: 'date',
          mode: 'range'
        }
      },
      // 推荐使用，比dateRange方便是，form value返回两个字段，后端比较好处理，，
      // "dateRange": [
      //   "2023-09-28 10:58:38",
      //   "2023-10-25 10:58:38"
      // ],
      // "startDate": "2023-09-28 10:58:35",
      // "endDate": "2023-10-25 10:58:35",
      // 不过目前会报错，才屏蔽
      // startDate: {
      //   type: 'string',
      //   ui: {
      //     widget: 'date',
      //     end: 'endDate'
      //   }
      // },
      // endDate: {
      //   type: 'string',
      //   ui: {
      //     widget: 'date'
      //   }
      // },
      inline: {
        type: 'string',
        ui: {
          widget: 'date',
          inline: true
        }
      },
      // 返回long类型的毫秒级别的时间戳， 13位长度
      datatimeNumber: {
        type: 'number',
        ui: {
          widget: 'date'
        }
      },
      yearNumber: {
        type: 'number',
        ui: {
          widget: 'date',
          mode: 'year',
          format: 'yyyy'
        }
      },
      // 多选框，一般用于选择多个选项，可以单选，也可以多选择，其值支持boolean，string，number类型
      //boolean类型，只能是单选，其值true或false， string和number支持多选，其值支持 基本类型数组，对象数组
      // 单个多选框, 通过boolean类型，单选框， yesOrNot，single 效果一样的

      // 在没有指定widget，默认为 nz-switch控件, 添加文字时，给一个状态描述，而不是一个字开或关
      switch: {
        type: 'boolean',
        title: 'nzSwitch',
        default: true,
        ui: {
          checkedChildren: '已开',
          unCheckedChildren: '已关'
        }
      },
      checkboxSingle: {
        type: 'boolean',
        title: 'checkboxSingle',
        description: '《用户协议》',
        readOnly: true,
        ui: {
          widget: 'checkbox'
        },
        default: true
      },
      // 多选框组
      checkboxArray: {
        type: 'string',
        title: 'checkboxArray',
        enum: ['Apple', 'Pear', 'Orange'],

        ui: {
          widget: 'checkbox',
          span: 6, // 指定每一项 8 个单元的布局
          checkAll: true
        },
        default: ['Apple']
      },
      // 多选框组
      checkboxObjectString: {
        type: 'string',
        enum: [
          { label: 'Apple', value: 'Apple' },
          { label: 'Pear', value: 'Pear' },
          { label: 'Orange', value: 'Orange' }
        ],
        ui: {
          widget: 'checkbox',
          span: 6, // 指定每一项 8 个单元的布局
          checkAll: true
        },
        default: ['Apple']
      },
      // 异步数据
      checkboxObjectNumber: {
        type: 'number',
        ui: {
          widget: 'checkbox',
          asyncData: () =>
            of([
              { label: 'Apple', value: 1 },
              { label: 'Pear', value: 2 },
              { label: 'Orange', value: 3 }
            ]).pipe(delay(200))
        },
        default: [1]
      },
      // 异步数据
      checkboxAsyncString: {
        type: 'string',
        ui: {
          widget: 'checkbox',
          asyncData: () =>
            of([
              { label: 'Apple', value: 'Apple' },
              { label: 'Pear', value: 'Pear' },
              { label: 'Orange', value: 'Orange' }
            ]).pipe(delay(200))
        },
        default: ['Apple']
      },

      // 单选框， 一般用多个多个选项中，只能选择一个,其值，推荐基本类型数组，对象数组
      radioBtn: {
        type: 'string',
        title: 'Button',
        enum: ['A', 'B', 'C'],
        ui: {
          widget: 'radio',
          styleType: 'button',
          buttonStyle: 'solid'
        },
        default: 'A'
      },
      // 异步数据
      async: {
        type: 'string',
        title: 'Async',
        ui: {
          widget: 'radio',
          asyncData: () =>
            of([
              { label: '男', value: 'M' },
              { label: '女', value: 'F' },
              { label: '未知', value: 'N' }
            ]).pipe(delay(100)),
          change: console.log
        },
        default: 'N'
      },
      status: {
        type: 'string',
        title: '状态',
        enum: [
          {
            label: '待支付',
            value: 'WAIT_BUYER_PAY'
          },
          {
            label: '已支付',
            value: 'TRADE_SUCCESS'
          },
          {
            label: '交易完成',
            value: 'TRADE_FINISHED'
          }
        ],
        default: 'WAIT_BUYER_PAY'
      },

      // cascader级联选择， 返回一个value的数组
      cascaderCity: {
        type: 'string',
        title: '选择地址',
        enum: [
          {
            value: 110000,
            label: '北京',
            parent: 0,
            children: [
              {
                value: 110100,
                label: '北京市',
                parent: 110000,
                children: [
                  {
                    value: 110101,
                    label: '东城区',
                    parent: 110100,
                    isLeaf: true
                  },
                  {
                    value: 110105,
                    label: '朝阳区',
                    parent: 110100,
                    isLeaf: true
                  }
                ]
              }
            ]
          }
        ],
        default: [110000, 110100, 110105],
        ui: 'cascader'
      },
      // cascader级联选择， 异步
      // 指每一次每一次选择会触发HTTP请求，数据来源于 asyncData；
      // 包含三个参数 (node: NzCascaderOption, index: number, me: CascaderWidget) => PromiseLike<any>，其中 me 表示当前小部件实例
      cascaderAsync: {
        type: 'number',
        title: 'RealTime',
        ui: {
          widget: 'cascader',
          asyncData: (node: any, index: any) => {
            return new Promise<void>(resolve => {
              setTimeout(() => {
                (node as any).children = [
                  { value: 110000, label: '北京', parent: 0 },
                  { value: 110100, label: '北京市', parent: 110000 },
                  { value: 110101, label: '东城区', parent: 110100 },
                  { value: 110105, label: '朝阳区', parent: 110100 },
                  { value: 310000, label: '上海', parent: 0 },
                  { value: 310100, label: '上海市', parent: 310000 },
                  { value: 310101, label: '黄浦区', parent: 310100 },
                  { value: 310104, label: '徐汇区', parent: 310100 }
                ].filter((w: any) => {
                  w.isLeaf = index === 1;
                  return w.parent === (node.value || 0);
                });
                resolve();
              }, 300);
            });
          }
        },
        default: [110000, 110100, 110105]
      },

      // 滑动输入条
      // 返回一个数字， "singleSlider": 19,
      singleSlider: {
        type: 'number',
        title: '单滑输入条',
        ui: {
          widget: 'slider',
          grid: {
            span: 16
          }
        },
        default: 10
      },
      // 双滑块模式， 返回一个数字数组
      // "range": [
      //   10,
      //   31
      // ],
      doubleSlider: {
        type: 'number',
        title: '双滑输入条',
        ui: {
          widget: 'slider',
          // 指定按范围滑动，支持双滑
          range: true,
          grid: {
            span: 16
          }
        },
        default: [10, 20]
      },
      // 标签选择
      like: {
        type: 'string',
        title: '兴趣1',
        enum: [
          {
            value: 1,
            label: '电影'
          },
          {
            value: 2,
            label: '书'
          },
          {
            value: 3,
            label: '旅行'
          }
        ],
        default: 2,
        ui: {
          widget: 'tag'
        }
      },
      // 支持图标显示
      icon: {
        type: 'number',
        title: '兴趣2',
        enum: [
          { value: 1, label: 'Twitter', prefixIcon: { type: 'twitter' } as SFIcon },
          { value: 2, label: 'Facebook', suffixIcon: { type: 'facebook' } as SFIcon }
        ],
        ui: {
          widget: 'tag'
        }
      },

      // rate评分
      rate: {
        type: 'number',
        title: '评级',
        // 星的最大个数，默认5个
        maximum: 5,
        // 每次点击，支持半选， 默认0.5半选
        multipleOf: 0.5,
        default: 4.5,
        ui: {
          widget: 'rate'
        }
      },
      // rate允许半选
      rate2: {
        type: 'number',
        title: '评级',
        default: 4.5,
        ui: {
          widget: 'rate',
          // value为选择的值
          text: '{{value}} starts'
        }
      },
      // 双栏穿梭选择框
      roles: {
        type: 'number',
        title: '角色',
        enum: [
          {
            title: 'DNS管理',
            value: 10
          },
          {
            title: 'ECS管理',
            value: 11
          },
          {
            title: 'OSS管理',
            value: 12
          },
          {
            title: 'RDS管理',
            value: 13
          }
        ],
        ui: {
          widget: 'transfer',
          titles: ['未拥有', '已拥有'],
          grid: {
            span: 12
          }
        },
        default: [11, 12]
      },
      // 基于异步加载数据，delay(5000)表示从订阅开始延迟5秒，显示到界面
      roles2: {
        type: 'number',
        title: '角色',
        ui: {
          widget: 'transfer',
          titles: ['未拥有', '已拥有'],
          asyncData: () =>
            of([
              { title: 'DNS管理', value: 10 },
              { title: 'ECS管理', value: 11 },
              { title: 'OSS管理', value: 12 },
              { title: 'RDS管理', value: 13 }
            ]).pipe(delay(5000))
        },
        default: [11, 12]
      },

      // 单文件上传
      // 后台访问
      // {
      //   "fileName": "202309/465909681594437_BDR2FD1214.jpg",
      //   "name": "BDR2FD1214.jpg",
      //   "url": "/api/code-tea/ext/material/download/file/202309/465909681594437_BDR2FD1214.jpg",
      //   "status": true
      // }
      // 更多信息，参考： https://ng-alain.com/form/upload/zh
      singleFileUpload: {
        type: 'string',
        title: '单文件上传',
        enum: [
          {
            uid: -1,
            name: 'xxx.png',
            status: 'done',
            url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
            response: {
              resource_id: 1
            }
          }
        ],
        ui: {
          // 指定选择类型， select点击选择，drag，还是拖动模式， 默认 select
          type: 'select',
          widget: 'upload',
          action: this.actionUrl,
          // 用于解决下载文件或预览时，后天能通过认证， 经测试并没用， 解决方式还是后端跳过认证
          // 总结： 这个请求头可上传动作添加，经测试不适用下载文件或预览时，基于上面结论，可以不用设置
          // 这里设置认证的token，基于上面结论，可以不用设置，除非其他请求头
          headers: { Authorization: `bearer ${this.token}` },
          // 上传指定请求参数
          data: { type: 11 },
          //上传请求时是否携带 cookie, 默认true
          withCredentials: false,
          //false单选， true多选， 默认单选， 表单只会记录最后一次上传，
          multiple: false,
          // 限制每次选择上传文件数，提示并不是总的文件数， multiple 为true有效
          limit: 1,
          //限制文件类型
          fileType: 'image/png,image/jpeg,image/gif,image/bmp',
          //限制文件大小，单位：KB；0 表示不限， 超过设定不能上传界面无反应
          fileSize: 0,
          //上传列表的内建样式, text,picture,picture-card, 默认 text, text和picture都是在按钮下方显示， picture-card，替代按钮地方并以正方形显示
          listType: 'text',
          // 已上传文件列表
          // fileList:'',
          // 重命名返回参数，支持 a.b.c 的嵌套写法，若不指定表示整个返回体, 不知道干嘛用 ？？？
          resReName: 'resource_id',
          // 重命名预览图像URL返回参数，支持 a.b.c 的嵌套写法，若不指定表示使用文件对象的 url、thumbUrl 值 ？？？
          urlReName: 'url',
          //是否展示列表, 可设为一个对象，用于单独设定 showPreviewIcon 和 showRemoveIcon
          //支持 true， {showPreviewIcon: true}
          showUploadList: true,
          //发到后台的文件参数名, 默认file，后端使用file参数名来接收，java代码： @RequestParam("file") MultipartFile file
          name: 'file',
          //支持上传文件夹, true可支持上传整个文件夹， 后端如何接收 ？？？
          directory: false,
          //点击打开文件对话框, 默认true，false适合开发只查看图片的页面需求
          openFileDialogOnClick: true,
          showbutton: false
        }
      },

      // 支持多选文件上传， 每次只能上传2个
      // "mulitFileUpload": [
      //   {
      //     "fileName": "202309/465922958827590_BDR2FD1214.jpg",
      //     "name": "BDR2FD1214.jpg",
      //     "url": "/api/code-tea/ext/material/download/file/202309/465922958827590_BDR2FD1214.jpg",
      //     "status": true
      //   },
      //   {
      //     "fileName": "202309/465922958827589_f4dbeccb5313689c2a07a655ea64409a.jpeg",
      //     "name": "f4dbeccb5313689c2a07a655ea64409a.jpeg",
      //     "url": "/api/code-tea/ext/material/download/file/202309/465922958827589_f4dbeccb5313689c2a07a655ea64409a.jpeg",
      //     "status": true
      //   }
      // ],
      // 问题描述： picture-card 样式并没有正方形显示，而只是长方型
      mulitFileUpload: {
        type: 'string',
        title: '多个文件上海',
        ui: {
          type: 'select',
          widget: 'upload',
          action: this.actionUrl,
          data: { type: 22 },
          multiple: true,
          limit: 1,
          // nz-upload 用于控制总的上传文件数超过2个，则不显示上传按钮， 不过在这里没有生效 ???, 可能不支持，文档中无
          showButton: this.fileList.length < 2,
          fileList: this.fileList,
          listType: 'picture-card'
        }
      },
      // 拖动模式
      dragFileUpload: {
        type: 'string',
        title: 'Drag',
        ui: {
          type: 'drag',
          widget: 'upload',
          action: this.actionUrl,
          data: { type: 33 },
          listType: 'picture'
        }
      },

      // 数组类型，界面有一个‘添加’按钮, 用于添加子表数据，返回对象数组
      products: {
        type: 'array',
        title: '产品清单',
        maxItems: 5,
        items: {
          type: 'object',
          properties: {
            name: {
              type: 'string',
              title: '名称'
            },
            price: {
              type: 'number',
              title: '产品单价',
              minimum: 1
            }
          },
          required: ['name', 'price'],
          ui: {
            spanLabel: 3,
            spanControl: 19,
            grid: {
              //每个product下name, price 占格数，24表示每个元素都是整行， 默认为24
              span: 24
            }
          }
        },
        ui: {
          grid: {
            //单独设置products元素在整个表单所占格数，24表示整行
            span: 24,
            // 每个数组位格数，默认一样显示24/8=3个
            arraySpan: 8
          }
        }
      }
      // content: {
      //   type: 'string',
      //   title: '内容',
      //   ui: {
      //     widget: 'tinymce',
      //     grid: {
      //       span: 24
      //     }
      //   }
      // }
    },
    required: ['name', 'email', 'singleFileUpload'],
    ui: {
      spanLabelFixed: 100,
      grid: {
        span: 12
      }
    },
    type: 'object'
  };

  formData = {
    email: 'zhenyun.su@qq.com'
  };

  ui = {};

  layout: SFLayout = 'horizontal';

  get formMeta() {
    return JSON.stringify(this.schema, null, 2);
  }

  formValue: string = '';
  get formUI() {
    return JSON.stringify(this.ui, null, 2);
  }

  formMetaChange(value: any) {
    this.schema = JSON.parse(value);
  }
  newFormValue: any;
  formValueChange(value: any) {
    this.newFormValue = JSON.parse(value);
  }
  formUIChange(value: any) {
    this.ui = JSON.parse(value);
  }
  schemaAction() {
    if (this.selectedIndex === 2) {
      this.formData = this.newFormValue;
      console.log('this.formData', this.formData);
    }
  }

  sfFormsubmit(value: any) {
    this.msg.msg_success(JSON.stringify(value));
    // 由于sf，提交按钮会自动根据formError来判断是否可用，下面代码无需使用
    // if (value.length >= 1) {
    //   this.msg.msg_error(JSON.stringify(this.formErrorInfo));
    // }
  }

  sfFormchange(value: any) {
    // console.log('formChange', value);
    this.formValue = JSON.stringify(value, null, 2);
  }

  formErrorInfo: any[] = [];
  sfFormError(value: any) {
    this.formErrorInfo = value;
    //debug可查看错误信息
    // if (value.length >= 1) {
    //   this.msg.msg_error(JSON.stringify(this.formErrorInfo));
    // }
  }

  layoutChange(value: any) {
    this.layout = value;
  }
  selectedSubIndex = 0;
  indexTs = `import type { NzSafeAny } from 'ng-zorro-antd/core/types';

import { SFUISchemaItem } from './ui';

export interface SFSchemaDefinition {
  [key: string]: SFSchema;
}

export interface SFSchemaEnum {
  [key: string]: NzSafeAny;

  /** 是否禁用状态 */
  disabled?: boolean;

  /** 文本 */
  label?: NzSafeAny;

  /** 文本 */
  title?: NzSafeAny;

  /** 值 */
  value?: NzSafeAny;

  /**
   * 主键，适用部分小部件数据键名，例如：'tree-select'
   */
  key?: NzSafeAny;

  /** 是否选中 */
  checked?: boolean;

  /**
   * 组名，适用部分允许组列表的小部件，例如：'select'
   * - 组对应的文本为 'label'
   * - 'children' 为子项
   */
  group?: boolean;

  /**
   * Whether to hide item
   *
   * 是否隐藏项
   */
  hide?: boolean;

  isLeaf?: boolean;

  /** 组对应的子类 */
  children?: SFSchemaEnum[];
}

export type SFSchemaType = 'number' | 'integer' | 'string' | 'boolean' | 'object' | 'array';

export type SFSchemaEnumType = SFSchemaEnum | number | string | boolean;

/**
 * JSON Schema Form 结构体
 *
 * **注意：** 所有结构都以标准为基准，除了 'ui' 属性为非标准单纯只是为了更好的开发
 */
export interface SFSchema {
  [key: string]: NzSafeAny;
  //////////// Any /////////////
  /**
   * 数据类型，支持 JavaScript 基础类型；注意项：
   *
   * - 'integer' 表示整型，'number' 表示浮点型
   * - JSON 中 'date' 等同 'string' 类型
   * - 指定 'format' 标准参数可以自动适配渲染小部件
   * - 指定 'widget' 参数强制渲染小部件
   */
  type?: SFSchemaType;
  /**
   * 枚举，静态数据源，例如：'radio'、'checkbox' 等
   *
   * - 'disabled' 属性表示：禁用状态
   * - 'label' 属性表示：文本
   * - 'value' 属性表示：返回值
   * - 基础数据类型数组会自动转化成 'SFSchemaEnum' 数组格式
   */
  enum?: SFSchemaEnumType[];
  //////////// 数值类型 /////////////
  /**
   * 最小值
   */
  minimum?: number;
  /**
   * 约束是否包括 'minimum' 值
   */
  exclusiveMinimum?: boolean;
  /**
   * 最大值
   */
  maximum?: number;
  /**
   * 约束是否包括 'maximum' 值
   */
  exclusiveMaximum?: boolean;
  /**
   * 倍数
   */
  multipleOf?: number;
  //////////// 字符串类型/////////////
  /**
   * 定义字符串的最大长度
   */
  maxLength?: number;
  /**
   * 定义字符串的最小长度
   */
  minLength?: number;
  /**
   * 验证输入字段正则表达式字符串
   */
  pattern?: string;
  //////////// 数组类型/////////////
  /**
   * 数组元素类型描述，只支持数组对象，若需要基础类型数组可通过其他部件支持
   *
   * '''json
   * items: {
   *   type: 'object',
   *   properties: {
   *     name: { type: 'string' },
   *     age: { type: 'number' }
   *   }
   * }
   * '''
   *
   * 结果
   *
   * '''json
   * [
   *   { "name": "cipchk1", "age": 18 },
   *   { "name": "cipchk2", "age": 16 }
   * ]
   * '''
   */
  items?: SFSchema;
  /**
   * 约束数组最小的元素个数
   * - 'type="array"' 时有效
   */
  minItems?: number;
  /**
   * 约束数组最大的元素个数
   * - 'type="array"' 时有效
   */
  maxItems?: number;
  /**
   * 约束数组每个元素都不相同
   * - 'type="array"' 时有效
   */
  uniqueItems?: boolean;
  /**
   * 数组额外元素的校验规则
   */
  additionalItems?: SFSchema;
  //////////// 对象类型/////////////
  /**
   * 最大属性个数，必须是非负整数
   */
  maxProperties?: number;
  /**
   * 最小属性个数，必须是非负整数
   */
  minProperties?: number;
  /**
   * 必填项属性
   */
  required?: string[];
  /**
   * 定义属性
   */
  properties?: { [key: string]: SFSchema };
  //////////// 条件类/////////////
  // 未来可能被移除
  // dependencies?: { [key: string]: string[] | SFSchema };
  /**
   * 条件验证
   * - 必须包含 'properties' 节点
   *  - 键名必须是当前节点 'properties' 值之一
   *  - 利用 'enum' 属性表示条件值，支持 '$ANY$' 表示任意值
   * - 不支持跨 Schema 节点
   * - 当条件成功会执行 'then' 否则执行 'else'
   * - 'if'和'then' 是必须同时出现，'else' 可选项
   */
  if?: SFSchema;
  /**
   * 条件成功时执行
   * - 只支持 'required' 参数，用于表示显示
   */
  then?: SFSchema;
  /**
   * 条件失败时执行
   * - 只支持 'required' 参数，用于表示显示
   */
  else?: SFSchema;
  //////////// 逻辑类/////////////
  /** **不建议** 使用，可用 'required' 替代 */
  allOf?: SFSchema[];
  /** **不建议** 使用，可用 'required' 和 'minProperties' 替代 */
  anyOf?: SFSchema[];
  /** 值必须是其中之一 */
  oneOf?: SFSchema[];
  //////////// 格式/////////////
  /**
   * 数据格式，[文档](http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.3)
   * - 'date-time' 日期时间，渲染为 'date'，[RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6)
   * - 'date'、'full-date' 日期，渲染为 'date'
   * - 'time'、'full-time' 时间，渲染为 'time'
   * - 'email' Email格式，渲染为 'autocomplete'
   * - 非标准：'week'，渲染为 'nz-week-picker'
   * - 非标准：'month'，渲染为 'nz-month-picker'
   * - 'ip' IP地址，渲染为 'input'
   * - 'uri' URL地址，渲染为 'upload'
   * - 'mobile' 手机号
   * - 'id-card' 身份证
   * - 'color' 颜色值
   */
  format?: string;
  //////////// 注释/////////////
  /**
   * 属性描述，相当于 'label' 值，按以下规则展示：
   * - 当值为 'null'、'undefined' 时使用 'key' 替代
   * - 当值为 '''' 空字符串表示不展示 'label' 部分，例如：'checkbox' 可能需要
   */
  title?: string | null;
  /**
   * 属性目的性解释
   */
  description?: string;
  /**
   * 默认值
   */
  default?: NzSafeAny;
  /**
   * 是否只读状态
   */
  readOnly?: boolean;
  //////////// 其他/////////////
  //////////// Definitions /////////////
  // /** 指定 Schema JSON 模式，默认为：'http://json-schema.org/draft-07/schema' */
  // $schema?: string;
  /** 内部类型定义体 */
  definitions?: SFSchemaDefinition;
  /** 引用定义体 */
  $ref?: string;
  // $schema?: string;
  /** 针对开发者的注释，无任何意义，也不会被校验 */
  $comment?: string;
  //////////// 非标准/////////////
  /** **唯一非标准：** 指定UI配置信息，优先级高于 'sf' 组件 'ui' 属性值 */
  ui?: SFUISchemaItem | string;
}  
  `;
  uiTs = `import { TemplateRef } from '@angular/core';

import type { ACLCanType } from '@delon/acl';
import type { NgClassType, NgStyleInterface, NzSafeAny, NzSizeLDSType } from 'ng-zorro-antd/core/types';

import type { ErrorSchema } from '../errors';
import type { SFValue } from '../interface';
import type { FormProperty } from '../model/form.property';

export type SFPlacement =
  | 'top'
  | 'left'
  | 'right'
  | 'bottom'
  | 'topLeft'
  | 'topRight'
  | 'bottomLeft'
  | 'bottomRight'
  | 'leftTop'
  | 'leftBottom'
  | 'rightTop'
  | 'rightBottom';

export type SFTrigger = 'click' | 'focus' | 'hover';

export type SFLSSize = 'large' | 'small';

export type SFDLSSize = NzSizeLDSType;

export interface SFGridSizeSchema {
  span?: number | null;
  order?: number | null;
  offset?: number | null;
  push?: number | null;
  pull?: number | null;
}

export interface SFGridSchema {
  /**
   * 栅格间隔
   */
  gutter?: number | null;
  /**
   * 栅格占位格数，为 '0' 时相当于 'display: none'
   */
  span?: number | null;
  /**
   * 数组索引栅格占位格数，为 '0' 时相当于 'display: none'，限 'type: 'array'' 时有效
   */
  arraySpan?: number | null;
  /**
   * 栅格左侧的间隔格数，间隔内不可以有栅格
   */
  offset?: number | null;
  xs?: number | SFGridSizeSchema;
  sm?: number | SFGridSizeSchema;
  md?: number | SFGridSizeSchema;
  lg?: number | SFGridSizeSchema;
  xl?: number | SFGridSizeSchema;
  xxl?: number | SFGridSizeSchema;
}

export type SFBuiltInWidgets =
  | 'text'
  | 'string'
  | 'number'
  | 'integer'
  | 'date'
  | 'time'
  | 'radio'
  | 'checkbox'
  | 'boolean'
  | 'textarea'
  | 'select'
  | 'tree-select'
  | 'tag'
  | 'upload'
  | 'transfer'
  | 'slider'
  | 'rate'
  | 'autocomplete'
  | 'cascader'
  | 'mention'
  | 'custom'
  | 'array'
  | 'object';

export interface SFRenderSchema {
  /**
   * 指定采用什么小部件渲染，所有小部件名可[查阅文档](https://ng-alain.com/)
   */
  widget?: SFBuiltInWidgets | (string & {});
  /**
   * 自定义类，等同 '[ngClass]' 值
   */
  class?: NgClassType;
  /**
   * 元素组件大小
   */
  size?: SFDLSSize;
  /**
   * 指定宽度，单位：'px'
   */
  width?: number;
  /**
   * 响应式属性
   */
  grid?: SFGridSchema;
  /**
   * 标签可选信息
   */
  optional?: string;
  /**
   * 标签可选帮助，使用 'nz-tooltip' 展示
   */
  optionalHelp?: string | SFOptionalHelp;
}

export interface SFOptionalHelp {
  text?: string;
  i18n?: string;
  /** 文字提示背景颜色 */
  bgColor?: string;
  /** 图标，默认：'question-circle' */
  icon?: string;
  placement?: SFPlacement;
  trigger?: SFTrigger;
  mouseEnterDelay?: number;
  mouseLeaveDelay?: number;
  overlayClassName?: string;
  overlayStyle?: NgStyleInterface;
}

export interface SFHorizontalLayoutSchema {
  /**
   * 'label' 栅格占位格数，默认：'5'
   * - '0' 时相当于 'display: none'
   * - 限 'horizontal' 水平布局有效
   */
  spanLabel?: number | null;

  /**
   * 'control' 栅格占位格数，默认：'19'
   * - '0' 时相当于 'display: none'
   * - 限 'horizontal' 水平布局有效
   */
  spanControl?: number | null;

  /**
   * 'control' 栅格左侧的间隔格数，间隔内不可以有栅格
   * - 限 'horizontal' 水平布局有效
   */
  offsetControl?: number | null;

  /**
   * 'label' 固定宽度
   * - 限 'horizontal' 水平布局有效
   */
  spanLabelFixed?: number | null;
}

export interface SFSchemaI18n {
  /**
   * 指 'schema.title' 的国际化键值
   */
  i18n?: string;
  /**
   * 对应 'schema.description' 国际化
   */
  descriptionI18n?: string;
}

export interface SFVisibleIfReturn {
  show?: boolean;
  required?: boolean;
}

export interface SFVisibleIf {
  [key: string]: NzSafeAny[] | ((value: SFValue, property: FormProperty) => boolean | SFVisibleIfReturn | null);
}

/** 指定如何渲染 'Schema' */
export interface SFUISchemaItem extends SFRenderSchema, SFHorizontalLayoutSchema, ErrorSchema, SFSchemaI18n {
  [key: string]: NzSafeAny;

  /**
   * Whether to enable the debug mode, the trust information will be printed out when the data is changed and verified, it is not recommended to use it in the production environment
   *
   * 是否开启调试模式，在数据变更、校验会打印出相信信息，不建议在生产环境中使用
   */
  debug?: boolean;

  /**
   * Order of property
   *
   * 属性顺序，当你只想某几个属性靠前时，则允许使用通配符 '*' 来表示剩余部分，且只允许出现一次
   *
   * @example
   *
   * [ 'a', 'b', 'c', 'd' ] + [ 'c', 'b', '*' ] = [ 'c', 'b', 'a', 'd']
   */
  order?: string[];
  /**
   * Whether to hide
   *
   * 是否隐藏
   */
  hidden?: boolean;
  /**
   * Is visible with conditions (When there are multiple, you can specify logic vis 'visibleIfLogical')
   *
   * 指定条件时才显示（当多个时可以通过 'visibleIfLogical' 来指定处理逻辑），但需要**注意**：
   * - 键值表示监听对象属性名
   * - JSON Schema 校验是各属性独立运行，监听对象属性每一次值变化都会重新做一次整个JSON结构计算
   *
   * 有效格式包括：
   * - 'visibleIf: { shown: [ true ] }'：当 'shown: true' 时才显示当前属性
   * - 'visibleIf: { shown: [ '$ANY$' ] }'：当 'shown' 包括任意值时
   * - 'visibleIf: { shown: (value: any, property: FormProperty) => value > 0 }'：复杂表达式
   */
  visibleIf?: SFVisibleIf;

  /**
   * The logical used when specifying multiple 'visibleIf', default: 'or'
   *
   * 指定多个 'visibleIf' 时采用的逻辑关系，默认：'or'
   */
  visibleIfLogical?: 'or' | 'and';

  /**
   * ACL 配置
   */
  acl?: ACLCanType | null;
}

/**
 * UI Schema，KEY名**务必**是 '$' 开头（例如：'$name'、'$id'），以便能区分KEY值还是UI选项
 * - 结构层级应同 'SFSchema' 一致
 * - 当KEY为 '*' 时表示对所有子表单元素都有效
 */
export interface SFUISchema {
  [key: string]: SFUISchemaItem | SFUISchemaItemRun;
}

/**
 * @inner
 */
export interface SFUISchemaItemRun extends SFUISchemaItem {
  /** @internal 自定义模板 */
  _render?: TemplateRef<void>;
  /** @internal 是否必填 */
  _required?: boolean;
}
  `;
}
